package org.easy4j.framework.core.orm.sql;

import com.google.common.reflect.TypeToken;
import org.easy4j.framework.core.config.Config;
import org.easy4j.framework.core.orm.EntityMapping;
import org.easy4j.framework.core.orm.Mapping;
import org.easy4j.framework.core.orm.annotation.Table;
import org.easy4j.framework.core.util.ClassScanner;
import org.springframework.core.io.AbstractResource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.util.ReflectionUtils;

import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Date;
import java.util.List;

/**
 * @author bjliuyong
 * @version 1.0
 * @created date 16-6-19
 */
public final class ExportSchemaUtil {


    public static void export(DataSource dataSource){

        Connection connection = null ;
        try {


            List<Class<?>> tables = ClassScanner.getClassListByAnnotationType(Config.getValue(Config.PROJECT_BASE_PACKAGE), Table.class);
            connection            = DataSourceUtils.getConnection(dataSource);
            ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();

            for(Class entityClass : tables){
                databasePopulator.addScript(new ExportEncodedResource(entityClass));
            }

            databasePopulator.populate(connection);

        } catch (Exception e) {
            new RuntimeException(e);
        } finally {
            DataSourceUtils.releaseConnection(connection ,dataSource);
        }
    }

    public static String getSchema(Class entityClass) {
        EntityMapping.initMapping(entityClass);
        String tableName = EntityMapping.getTableName(entityClass);
        Mapping mapping  = EntityMapping.getMapping(entityClass)  ;
        StringBuilder stringBuilder = new StringBuilder();

        stringBuilder.append("create table ").append(tableName).append("(\n");
        String[] columns = mapping.getColumns() ;
        String[] fields  = mapping.getFields();
        for(int i = 0 ; i < columns.length ;i++) {

            String column = columns[i] ;
            String field  = fields[i];

            Class cType  = ReflectionUtils.findField(entityClass, field).getType();

            TypeToken typeToken = TypeToken.of(cType);

            String sType = "varchar(40)" ;

            if( typeToken.isAssignableFrom(long.class) ) {
                sType  = "bigint" ;
            } else if(typeToken.isAssignableFrom(Date.class)) {
                sType  = "datetime" ;
            } else if(typeToken.isAssignableFrom(int.class)) {
                sType  = "int" ;
            }

            stringBuilder.append("`").append(column).append("` ").append(sType).append(" ");

            if(Mapping.ID_INDEX == i && mapping.hasId() ) {
                stringBuilder.append("auto_increment comment '主键', \n") ;
                continue;
            }
            stringBuilder.append(" comment '姓名',\n") ;

        }

        if(mapping.hasId()) {
            stringBuilder.append(" primary key (").append(columns[Mapping.ID_INDEX]).append(")\n");
        }

        stringBuilder.append(") comment='export_table';\n");

        return stringBuilder.toString();
    }

    private static class ExportEncodedResource extends AbstractResource {

        private final Class entityClass ;

        private ExportEncodedResource(Class entityClass) {
            this.entityClass = entityClass;
        }


        /**
         * Return a description for this resource,
         * to be used for error output when working with the resource.
         * <p>Implementations are also encouraged to return this value
         * from their {@code toString} method.
         *
         * @see Object#toString()
         */
        @Override
        public String getDescription() {
            return entityClass.getName();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            byte[] inputData = getSchema(entityClass).getBytes(Config.CHARSET_NAME) ;
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputData);
            return byteArrayInputStream;
        }
    }



}
